package merisis.silo;

import merisis.OutOfScaleError;

public class CakedCursorUtil {

	private static final long[] LEVEL_BOUNDARY = {

	31L, 992L, 30783L,

	954304L, 29583455L, 917087136L,

	28429701247L, 881320738688L, 27320942899359L,

	846949229880160L, 26255426126284991L, 813918209914834752L };

	public final static byte MAX_LEVEL = 11;

	public final static byte MAX_SLOT_NUM = (byte) 31;

	public static long toSequence(byte[] offsets) {

		long pos = offsets[0];
		byte level = (byte) (offsets.length - 1);

		long power = 1;
		for (int ph = 1; ph <= level; ph++) {

			power *= 31;
			pos += offsets[ph] * power;
		}

		if (level > 0) {
			pos += LEVEL_BOUNDARY[level - 1];
		}

		return pos;
	}

	public static byte[] toOffset(long pos) {

		byte level = 0;
		long r;
		if (pos < 29583455L) {
			if (pos < 30783L) {
				if (pos < 992L) {
					if (pos < 31L) {
						level = 0;
						r = pos;
					} else {
						level = 1;
						r = pos - 31L;
					}
				} else {
					level = 2;
					r = pos - 992L;
				}
			} else {
				if (pos < 954304L) {
					level = 3;
					r = pos - 30783L;
				} else {
					level = 4;
					r = pos - 954304L;
				}
			}
		} else {
			if (pos < 27320942899359L) {
				if (pos < 28429701247L) {
					if (pos < 917087136L) {
						level = 5;
						r = pos - 29583455L;
					} else {
						level = 6;
						r = pos - 917087136L;
					}
				} else {
					if (pos < 881320738688L) {
						level = 7;
						r = pos - 28429701247L;
					} else {
						level = 8;
						r = pos - 881320738688L;
					}
				}
			} else {
				if (pos < 26255426126284991L) {
					if (pos < 846949229880160L) {
						level = 9;
						r = pos - 27320942899359L;

					} else {
						level = 10;
						r = pos - 846949229880160L;
					}
				} else {
					if (pos < 813918209914834752L) {
						level = 11;
						r = pos - 26255426126284991L;
					} else {
						throw new IndexOutOfBoundsException(
								"overflow! max_level" + MAX_LEVEL
										+ ", max_num="
										+ LEVEL_BOUNDARY[MAX_LEVEL]);
					}
				}
			}
		}

		byte[] offset = new byte[level + 1];

		byte phase = 0;
		while (true) {

			offset[phase] = (byte) (r % MAX_SLOT_NUM);
			r /= MAX_SLOT_NUM;
			if (r == 0)
				break;

			phase++; // next level
		}

		return offset;
	}

	public static byte[] minValueOfLevel(byte level) {

		byte[] val;
		val = new byte[level + 1]; // reset to 0;

		return val;
	}

	public static byte[] maxValueOfLevel(byte level) {

		byte[] val;
		val = new byte[level + 1]; // reset to 0;

		final byte maxdigit = MAX_SLOT_NUM - 1;
		for (byte i = level; i >= 0; i--) {
			val[i] = maxdigit;
		}

		return val;
	}

	public static byte increase(byte[] ay, byte phase) {

		byte len = (byte) ay.length;

		// clear the lower digits
		for (byte k = 0; k < phase; k++) {
			ay[k] = 0;
		}

		byte i = phase;
		for (; i < len; i++) {

			byte digit = ay[i];
			digit++;
			if (digit < MAX_SLOT_NUM) {

				ay[i] = digit;

				return i;
			}

			ay[i] = 0; // accumulative carry
		}

		return i;
	}

	public static byte decrease(final byte[] ay, byte phase) {

		byte len = (byte) ay.length;

		// clear the lower digits
		for (byte k = 0; k < phase; k++) {
			ay[k] = MAX_SLOT_NUM - 1;
		}

		byte i = phase;
		for (; i < len; i++) {

			if (ay[i] > 0) {
				ay[i]--;
				return i;
			}
			ay[i] = MAX_SLOT_NUM - 1;
		}

		return i;
	}

	public static byte[] middleOffset(byte[] a, byte[] b) {

		long mid = (toSequence(a) + toSequence(b)) >> 1;

		return toOffset(mid);
	}

}
